/*
 * $Id: ssl.c 114 2005-05-12 05:15:40Z bmoore $
 *
 * Copyright (c) 2004, 2005  Branden J. Moore.
 *
 * This file is part of MacBiff, and is free software; you can redistribute
 * it and/or modify it under the terms of the GNU General Public License
 * version 2 as published by the Free Software Foundation.
 *
 * MacBiff is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with MacBiff; if not, write to the Free Software Foundation, Inc., 59
 * Temple Place, Suite 330, Boston, MA  02111-1307 USA.
 *
 */

#include <pthread.h>
#include <openssl/err.h>
#include <openssl/ssl.h>
#include <openssl/rand.h>

#include "xmalloc.h"
#include "ssl.h"

/* Holds the OpenSSL Mutexes */
static pthread_mutex_t *mutex_buf = NULL;

static SSL_CTX *ctx;

/* Dynamic locking stuff */
struct CRYPTO_dynlock_value {
	pthread_mutex_t mutex;
};

static struct CRYPTO_dynlock_value* dyn_create_func( const char *file,
		int line )
{
	struct CRYPTO_dynlock_value *val;
	xmalloc(val, struct CRYPTO_dynlock_value, 1);

	pthread_mutex_init( &(val->mutex), NULL );
	return val;
}


static void dyn_lock_func( int mode, struct CRYPTO_dynlock_value *val,
		const char *file, int line )
{
	if ( mode & CRYPTO_LOCK ) {
		pthread_mutex_lock( &(val->mutex) );
	} else {
		pthread_mutex_unlock( &(val->mutex) );
	}
}


static void dyn_destroy_func( struct CRYPTO_dynlock_value *val,
		const char *file, int line )
{
	pthread_mutex_unlock( &(val->mutex) );
	xfree(val);
}


static unsigned long tid_func( void )
{
	return (unsigned long)pthread_self();
}


static void locking_func( int mode, int n, const char *file, int line )
{
	if ( mode & CRYPTO_LOCK ) {
		pthread_mutex_lock( &(mutex_buf[n]) );
	} else {
		pthread_mutex_unlock( &(mutex_buf[n]) );
	}
}


static void thread_setup( void )
{
	int i;

	xmalloc( mutex_buf, pthread_mutex_t, CRYPTO_num_locks() );
	for ( i = 0 ; i < CRYPTO_num_locks() ; ++i ) {
		pthread_mutex_init( &(mutex_buf[i]), NULL );
	}
	CRYPTO_set_id_callback( tid_func );
	CRYPTO_set_locking_callback( locking_func );

	CRYPTO_set_dynlock_create_callback( dyn_create_func );
	CRYPTO_set_dynlock_lock_callback( dyn_lock_func );
	CRYPTO_set_dynlock_destroy_callback( dyn_destroy_func );
}


int initialize_SSL( void )
{
	thread_setup();
	SSL_library_init();
	SSL_load_error_strings();

	while ( !RAND_status() ) {
		RAND_load_file( "/dev/random", 1024 );
	}

	if ( !(ctx = SSL_CTX_new( SSLv23_client_method() )) ) {
		alert("SSL_CTX_new returned %lu\n", ERR_get_error() );
		return 1;
	}
	SSL_CTX_set_options( ctx, SSL_OP_NO_SSLv2 );

	return 0;
}


SSL* get_new_SSL( void )
{
	return ctx ? SSL_new(ctx) : NULL;
}
